home *** CD-ROM | disk | FTP | other *** search
/ 9-Digit Zip Code Directory / 9-Digit Zip Code Directory (American Business Information) (ABIZIP-12).ISO / z4src.zip / DASORT.C < prev    next >
C/C++ Source or Header  |  1995-09-12  |  19KB  |  637 lines

  1. //----------------------------------------------------------------------------
  2. //                            MODULE DESCRIPTION
  3. //
  4. //  Module:    dasort.c
  5. //   Title:    Data File Build Utility Library
  6. //  Notice:    John M. Weeder
  7. //                 Copyright (c) 1993. All rights reserved.
  8. //             This module contains proprietary information and should be 
  9. //                treated as confidential.
  10. //
  11. //----------------------------------------------------------------------------
  12. //                           MAINTENANCE HISTORY
  13. //
  14. // $Workfile$
  15. // $Revision$
  16. //   $Author$
  17. //     $Date$
  18. //      $Log$    
  19. //
  20. //----------------------------------------------------------------------------
  21. //                             MODULE NARRATIVE
  22. //
  23. //
  24. //    This module contains sort the index file of the delimited data file.
  25. //
  26. //    Sort methodology:
  27. //        Assume there are N records
  28. //        Assign a fixed length key of size K to each record
  29. //        Define a sort area size of S bytes.
  30. //        The sort area will hold s = S / K records.
  31. //        Define P = (N / s) + 1 as the number of passes to be made. This is the
  32. //            number of sort buffers needed to hold all the records.
  33. //        If P > s, then abort because the final merge will not work
  34. //            efficiently.
  35. //        Make P passes at the data file. Each time sort the next
  36. //            s records in the file (in place). This is done by reading s keys
  37. //            into the sort buffer and using QuickSort to sort them. They are then
  38. //            written back to disk.
  39. //        Use the sort buffer as a cache. Read the first record from each sort
  40. //            pass into the buffer (P records). Select lowest record and write it
  41. //            to a temporary file.
  42. //            Update the cache with the next record from the pass from which the
  43. //            previous record was selected. Keep doing this until all records are
  44. //            processed.
  45. //        Delete current index and rename temporary index to be the current index.
  46. //
  47. //    The code in this module should be written entirely in C. 
  48. //    Do not use any C++ constructs.
  49. //
  50. //    This module is portable to:
  51. //        DOS 3.X+
  52. //        MS Windows 3.X+
  53. //        OS/2 2.X+
  54. //        OS/2 2.0 PM
  55. //        SCO UNIX.
  56. //
  57. //    The following compilers are supported:
  58. //        MSC 6.0A
  59. //        MSC/C++ 7.0
  60. //        Borland C++ 3.1 for DOS
  61. //        Borland C++ 1.0 for OS/2 2.X
  62. //        SCO UNIX cc
  63. //
  64. //----------------------------------------------------------------------------
  65. #include <da.h>
  66.  
  67.  
  68. //----------------------------------------------------------------------------
  69. //    Global data
  70. //----------------------------------------------------------------------------
  71. typedef struct _G        // Sort data
  72. {
  73.     PDATACFG pcfg;          // Configuration data
  74.     DLM dlm;          // Delimited file data.
  75.     HF hfTemp;          // Temporary file handle
  76.     CHAR szTemp[MAX_PATH];      // Temporary file name
  77.     SIZET cKeyLen;          // Length of key
  78.     SIZET cKeySort;          // Size of key to sort with
  79.     SIZET cRecsPerPart;      // Max records per partition//CHGD TO LONG
  80.     SIZET cPasses;          // Number of passes to make
  81.     LONG lRecs;          // Records in file
  82.     LONG lStart;          // Starting record in this partition
  83.     LONG lEnd;          // Ending record in this partition
  84.     PLONG plRecord;          // Pointer to cache record indicator
  85.     PBYTE pbPartition;      // Sort partition buffer
  86.     BOOL fSorted;          // File sorted indicator
  87.     PBYTE pbWrite;          // Index key write buffer
  88.     PBYTE pbWriteNext;      // Next space in buffer
  89.     SIZET cbWrite;          // Number of records in buffer
  90.     PSIZET pcMerge;          // Next space in buffer
  91. } G;
  92. BASETYPE(G);
  93. static G g;
  94.  
  95. #define MAX_WRITE_BUF    (1024)
  96. #define FL_CF                 (FL_TRUNCATE|FL_CREATE|FL_READWRITE\
  97.                                 |FL_DENYREADWRITE|FL_BINARY)
  98.  
  99. //----------------------------------------------------------------------------
  100. //    Prototypes
  101. //----------------------------------------------------------------------------
  102. static int __keysort__(const void *, const void *);
  103. static BOOL FN_L DataSortFile(void);
  104. static BOOL FN_L DataSortInitialize(void);
  105. static BOOL FN_L DataSortMerge(void);
  106. static BOOL FN_L DataSortMergeProcess(void);
  107. static BOOL FN_L DataSortMergeRead(SIZET, BOOL);
  108. static BOOL FN_L DataSortMergeWrite(PDLMNDX);
  109. static SHORT FN_L DataSortPartition(void);
  110. static BOOL FN_L DataSortTerminate(void);
  111. static BOOL FN_L DataSortWrite(void);
  112.  
  113.  
  114. //----------------------------------------------------------------------------
  115. //   Description:    Sort routine used by qsort to sort keys
  116. //    Parameters:    see qsort()
  117. //       Returns:    see qsort()
  118. //----------------------------------------------------------------------------
  119. static int __keysort__(const void *pv1, const void *pv2)
  120. {
  121.     return memcmp(pv1, pv2, g.cKeySort);
  122. }
  123.  
  124.  
  125. //----------------------------------------------------------------------------
  126. //   Description:    Sort a delimited data file.
  127. //    Parameters:    pcfg        Configuration file data.
  128. //       Returns:    TRUE if successful.
  129. //----------------------------------------------------------------------------
  130. BOOL FN_E DataSort(PDATACFG pcfg)
  131. {
  132.     BOOL fResult;
  133.     TIMET timetElapsed = time(NULL);
  134.  
  135.     Assert(pcfg);
  136.     memset(&g, 0, sizeof(g));                // Zero config data
  137.     g.hfTemp = -1;                                // Initialize variables
  138.     g.pcfg = pcfg;
  139.     if (!DataSortInitialize())                // Initialize
  140.         {
  141.         DataSortTerminate();
  142.         return FALSE;
  143.         }
  144. #if OS_DOS ||OS_OS2
  145.     if (g.pcfg->fVerbose)
  146.         Output("Press <ESC> to abort processing...\n");
  147. #endif
  148.     fResult = DataSortFile();                // Sort data
  149.     DataSortTerminate();                        // Done
  150.     if (g.pcfg->fVerbose)
  151.         {
  152.         timetElapsed = time(NULL) - timetElapsed;
  153.         Output("Elapsed time %ld days, %ld hours, %ld minutes, %ld seconds.\n",
  154.             DAYS(timetElapsed), HOURS(timetElapsed),
  155.             MINUTES(timetElapsed), SECONDS(timetElapsed));
  156.         }
  157.     return fResult;
  158. }
  159.  
  160.  
  161. //----------------------------------------------------------------------------
  162. //   Description:    Sort input file.
  163. //    Parameters:    
  164. //       Returns:    TRUE if successful.
  165. //----------------------------------------------------------------------------
  166. static BOOL FN_L DataSortFile(void)
  167. {
  168.     SHORT sResult;
  169.     SIZET cPass;
  170.     LONG lRecords;
  171.  
  172.     g.fSorted = TRUE;
  173.     for (cPass = 0; cPass < g.cPasses; ++cPass)
  174.         {
  175.         while (KbdReady())
  176.             if (KbdChar() == '\x1B')
  177.                 {
  178.                 if (g.pcfg->fVerbose)
  179.                     Output("\r%79s\rProcessing aborted!\n", "");
  180.                 return FALSE;
  181.                 }
  182.  
  183.         if (g.pcfg->fVerbose)
  184. #if OS_UNIX
  185.             Output("Processing partition %6u of %u\n", cPass+1, g.cPasses);
  186. #else
  187.             Output("\rProcessing partition %6u of %u", cPass+1, g.cPasses);
  188. #endif
  189.  
  190.  
  191.         g.lStart = (LONG)cPass * (LONG)g.cRecsPerPart;
  192.         lRecords = (LONG)g.cRecsPerPart;
  193.         g.lEnd = g.lStart + lRecords - 1;
  194.         if (g.lEnd >= g.lRecs)
  195.             g.lEnd = g.lRecs - 1;
  196.         g.plRecord[(SIZET)cPass] = g.lStart;
  197.         sResult = DataSortPartition();
  198.         if (sResult < 0)
  199.             {
  200.             return FALSE;
  201.             }
  202.         else if (sResult == 0)
  203.             {
  204.             g.fSorted = FALSE;
  205.             if (!DataSortWrite())
  206.                 return FALSE;
  207.             }
  208.         }
  209.     if (g.pcfg->fVerbose)
  210.         Output("\rProcessing partition %6u of %u\n", cPass, g.cPasses);
  211.     if (g.cPasses > 1)                        // File was not sorted, so merge it.
  212.         {
  213.         if (!DataSortMerge())
  214.             return FALSE;
  215.         }
  216.     return TRUE;
  217. }
  218.  
  219.  
  220. //----------------------------------------------------------------------------
  221. //   Description:    Initialize for file sorting
  222. //    Parameters:
  223. //       Returns:    TRUE if successful.
  224. //----------------------------------------------------------------------------
  225. static BOOL FN_L DataSortInitialize(void)
  226. {
  227.  
  228.     SIZET cPartition;
  229.     LONG lPasses;
  230.  
  231.                                                     // Open delimited data files
  232.     if (!DataDelimitOpen(g.pcfg, &g.dlm)
  233.     || g.dlm.lRecs == 0)
  234.         return FALSE;
  235.  
  236.     if (g.dlm.lRecs <= 1)
  237.         return TRUE;
  238.  
  239.     strcpy(g.szTemp, g.pcfg->szDelimitNdx);
  240.     FnameAppendExt(g.szTemp, "$$$", TRUE);
  241.  
  242. #if OS_UNIX
  243.     cPartition = 8192 _K;
  244. #else
  245.     cPartition = (SIZET)63 _K;
  246. #endif
  247.  
  248.     g.cKeySort = DataKeyLen(g.pcfg);
  249.     g.cKeyLen = g.cKeySort + sizeof(DLMNDX);
  250.     g.lRecs = g.dlm.lRecs;
  251.     g.cRecsPerPart = (SIZET) (cPartition / g.cKeyLen);
  252.     if ((LONG)g.cRecsPerPart > g.lRecs)
  253.         g.cRecsPerPart = (SIZET)g.lRecs;
  254.     lPasses = g.lRecs / (LONG)g.cRecsPerPart;
  255.     if ((g.lRecs % (LONG)g.cRecsPerPart) != 0)
  256.         lPasses++;
  257.  
  258.     if (lPasses > (LONG)g.cRecsPerPart)
  259.         {
  260.     Error("Partition size is too small.");
  261.         return FALSE;
  262.         }
  263.     g.cPasses = (SIZET)lPasses;
  264.     g.plRecord = MemAlloc((SIZET)(g.cPasses * sizeof(LONG)));
  265.     g.pbPartition = MemAlloc(g.cRecsPerPart * g.cKeyLen);
  266.     g.pbWrite = MemAlloc(sizeof(DLMNDX) * MAX_WRITE_BUF);
  267.     g.pcMerge = MemAlloc((SIZET)((g.cPasses + 1) * sizeof(SIZET)));
  268.  
  269.     if (g.plRecord == NULL
  270.     || g.pbPartition == NULL
  271.     || g.pcMerge == NULL
  272.     || g.pbWrite == NULL)
  273.         return ErrorNoMem();
  274.     g.pbWriteNext = g.pbWrite;
  275.     if (g.pcfg->fVerbose)
  276.         {
  277.         Output("Sorting %ld records\n", g.lRecs);
  278.         Output("Key length is %u (%u) bytes\n", g.cKeySort, g.cKeyLen);
  279.         Output("%u partitions of %u records\n", g.cPasses, g.cRecsPerPart);
  280.         }
  281.     return TRUE;
  282. }
  283.  
  284.  
  285. //----------------------------------------------------------------------------
  286. //   Description:    Merge sorted partitions into one index file.
  287. //    Parameters:    
  288. //       Returns:    TRUE if successful.
  289. //----------------------------------------------------------------------------
  290. static BOOL FN_L DataSortMerge(void)
  291. {
  292.     SIZET cPass;
  293.     BOOL fResult;
  294.     PBYTE pb1, pb2;
  295.     PDLMNDX pdlmndx1,pdlmndx2;
  296.     DLMREC dlmrec;
  297.  
  298.     pb1 = g.pbPartition;                    // Check if file already sorted
  299.     pb2 = g.pbPartition + g.cKeyLen;
  300.  
  301.     for (cPass = 1; cPass < g.cPasses; ++cPass)
  302.         {
  303.         LONG lRec1, lRec2;
  304.  
  305.         lRec2 = g.plRecord[cPass];
  306.         pdlmndx2 = DataDelimitReadNdx(&g.dlm, lRec2);
  307.         if (pdlmndx2 == NULL
  308.         || DataDelimitReadRecordNdx(&g.dlm, lRec2, pdlmndx2) == NULL)
  309.             return FALSE;
  310.                                                 // Create key
  311.         DataDelimitConvert(&g.dlm, &dlmrec);
  312.         DataKey(&dlmrec, (PSZ)pb2);
  313.         lRec1 = lRec2 - 1;
  314.         pdlmndx1 = DataDelimitReadNdx(&g.dlm, lRec1);
  315.  
  316.         if (pdlmndx1 == NULL
  317.         || DataDelimitReadRecordNdx(&g.dlm, lRec1, pdlmndx1) == NULL) {
  318.             return FALSE;
  319.           }
  320.  
  321.         DataDelimitConvert(&g.dlm, &dlmrec);
  322.         DataKey(&dlmrec, (PSZ)pb1);
  323.         if (memcmp(pb1, pb2, g.cKeySort) > 0)
  324.             {
  325.             g.fSorted = FALSE;
  326.             break;
  327.             }
  328.         }
  329.     if (g.fSorted)        // If already sorted, early out
  330.         return TRUE;
  331.  
  332.     if (!FileOpen(&g.hfTemp, g.szTemp, FL_CF, NULL))
  333.         return FALSE;
  334.  
  335.     if (g.pcfg->fVerbose)
  336.         Output("\rOpened temporary index file '%s'\n", g.szTemp);
  337.  
  338.     for (cPass = 0; cPass <= g.cPasses; ++cPass)
  339.         g.pcMerge[cPass] = MAX_SIZET;
  340.                                                     // Preload cache
  341.     for (cPass = 0; cPass < g.cPasses; ++cPass)
  342.         if (!DataSortMergeRead(cPass, FALSE))
  343.             return FALSE;
  344.  
  345.     fResult = DataSortMergeProcess();    // Do the actual merge
  346.  
  347.     DataDelimitClose(&g.dlm);  // Rename temporary file to index
  348.     FileClose(g.hfTemp);       //   file name
  349.     g.hfTemp = -1;
  350.     if (fResult)
  351.         {
  352.         FnameDelete(g.pcfg->szDelimitNdx);
  353.         if (!FnameRename(g.szTemp, g.pcfg->szDelimitNdx))
  354.             return FALSE;
  355.         }
  356.     else if (g.szTemp[0] && FnameIsFile(g.szTemp))
  357.         FnameDelete(g.szTemp);
  358.  
  359.     return fResult;
  360. }
  361.  
  362.  
  363. //----------------------------------------------------------------------------
  364. //   Description:    Merge sorted partitions into one index file.
  365. //    Parameters:
  366. //       Returns:    TRUE if successful.
  367. //----------------------------------------------------------------------------
  368. static BOOL FN_L DataSortMergeProcess(void)
  369. {
  370.     LONG lRec;
  371.  
  372.     for (lRec = 0; lRec < g.lRecs; ++lRec)
  373.         {
  374.         SIZET cWhich;
  375.  
  376.         if (lRec == 0 || ((lRec+1) % 1000) == 0)
  377.             {
  378.             while (KbdReady())
  379.                 if (KbdChar() == '\x1B')
  380.                     {
  381.                     if (g.pcfg->fVerbose)
  382.                         Output("\r%79s\rProcessing aborted!\n", "");
  383.                     return FALSE;
  384.                     }
  385.  
  386.             if (g.pcfg->fVerbose)
  387. #if OS_UNIX
  388.                 Output("Merging record %8ld of %ld\n", lRec+1, g.lRecs);
  389. #else
  390.                 Output("\rMerging record %8ld of %ld", lRec+1, g.lRecs);
  391. #endif
  392.  
  393.             }
  394.         cWhich = g.pcMerge[0];                // Get lowest available element
  395.         Assert(cWhich != MAX_SIZET);
  396.         if (!DataSortMergeWrite((PDLMNDX)(g.pbPartition + cWhich * g.cKeyLen + g.cKeySort)))
  397.             return FALSE;
  398.                                                     // Read next record into cache
  399.         if (!DataSortMergeRead(cWhich, TRUE))
  400.             return FALSE;
  401.         }
  402.     if (!DataSortMergeWrite(NULL))        // Flush output
  403.         return FALSE;
  404.     if (g.pcfg->fVerbose)
  405.             Output("\rMerging record %8ld of %ld\n", lRec, g.lRecs);
  406.     return TRUE;
  407. }
  408.  
  409.  
  410. //----------------------------------------------------------------------------
  411. //   Description:    Read the next record for a partition.
  412. //             been set for each partition.
  413. //    Parameters:    cPass        Which partition
  414. //        fInc        If true, read next record, else current
  415. //       Returns:    TRUE if successful.
  416. //----------------------------------------------------------------------------
  417. static BOOL FN_L DataSortMergeRead(SIZET cPass, BOOL fInc)
  418. {
  419.     PBYTE pb1, pb2;
  420.     PDLMNDX pdlmndx1, pdlmndx2;
  421.     LONG lNext;
  422.     DLMREC dlmrec;
  423.     BOOL fEmpty = FALSE;                        // Partition empty?
  424.     PSIZET pc1, pc2;                            
  425.  
  426.     pc1 = pc2 = g.pcMerge;
  427.     if (fInc)
  428.         {
  429.        g.plRecord[cPass]++;                    // Move pointer to next record
  430.                                                     // Check if partition is used up
  431.        lNext = (LONG)(cPass + 1) * (LONG)g.cRecsPerPart;
  432.        if (g.plRecord[cPass] >= lNext
  433.        || g.plRecord[cPass] >= g.lRecs)
  434.            {                                        // No more records in this partition
  435.            g.plRecord[cPass] = -1;
  436.             fEmpty = TRUE;
  437.            }
  438.         }
  439.     if (!fEmpty)
  440.         {
  441.        pb1 = g.pbPartition + cPass * g.cKeyLen;
  442.        pdlmndx1 = (PDLMNDX)(pb1 + g.cKeySort);
  443.        pdlmndx2 = DataDelimitReadNdx(&g.dlm, g.plRecord[cPass]);
  444.        if (pdlmndx2 == NULL
  445.        || DataDelimitReadRecordNdx(&g.dlm, g.plRecord[cPass], pdlmndx2) == NULL)
  446.            return FALSE;
  447.    
  448.        DataDelimitConvert(&g.dlm, &dlmrec);
  449.         DataKey(&dlmrec, (PSZ)pb1);
  450.        *pdlmndx1 = *pdlmndx2;                // Store index info
  451.  
  452.         //
  453.         //    Insert entry in table in sorted order!
  454.         //
  455.         for ( ; *pc2 != MAX_SIZET && *pc2 != cPass; ++pc2)
  456.             {
  457.            pb2 = g.pbPartition + pc2[0] * g.cKeyLen;
  458.             if (memcmp(pb1, pb2, g.cKeySort) <= 0)
  459.                 {
  460.                 SIZET cSave = *pc2;            // Put in list in the place of current
  461.                 *pc2++ = cPass;                // element
  462.                 
  463.                 for ( ; *pc2 != MAX_SIZET; ++pc2)
  464.                     if (*pc2 == cPass)
  465.                         {
  466.                         *pc2 = cSave;
  467.                         return TRUE;
  468.                         }
  469.                     else                            // Propogate change of the list
  470.                         {                            // This happens on an insert only
  471.                         SIZET cTemp = *pc2;
  472.                         *pc2 = cSave;
  473.                         cSave = cTemp;
  474.                         }
  475.                 *pc2 = cSave;                    // Place displaced element at end of list
  476.                 return TRUE;
  477.                 }
  478.             }
  479.         if (*pc2 == MAX_SIZET)                // Partition was not in list
  480.             {                                        // Add it to the end of the list
  481.             *pc2 = cPass;
  482.             return TRUE;
  483.             }
  484.         //
  485.         //    We are at the previous sort location of this partition.
  486.         //    Insert the partition in its new location.
  487.         //
  488.         pc1 = pc2;
  489.         ++pc2;
  490.         for (; *pc2 != MAX_SIZET; ++pc2)
  491.             {
  492.            pb2 = g.pbPartition + pc2[0] * g.cKeyLen;
  493.             if (memcmp(pb1, pb2, g.cKeySort) <= 0)
  494.                 break;
  495.             *pc1++ = *pc2;                        // Copy down to make room
  496.             }
  497.         *pc1 = cPass;                            // Place element in list
  498.         }
  499.     else
  500.         {
  501.         for ( ;*pc2 != MAX_SIZET; ++pc2)    // Eliminate partition from merge
  502.             if (*pc2 != cPass)                //  list
  503.                 *pc1++ = *pc2;
  504.         *pc1 = MAX_SIZET;
  505.         }
  506.     return TRUE;
  507. }
  508.  
  509.  
  510. //----------------------------------------------------------------------------
  511. //   Description:    Write a merge record. Output is buffered
  512. //    Parameters:    pdlmndx            Record to write.
  513. //                                            If null, remaining records are flushed
  514. //       Returns:    TRUE if successful.
  515. //----------------------------------------------------------------------------
  516. static BOOL FN_L DataSortMergeWrite(PDLMNDX pdlmndx)
  517. {
  518.     if (pdlmndx != NULL)
  519.         {
  520.         memcpy(g.pbWriteNext, (PBYTE)pdlmndx, sizeof(DLMNDX));
  521.         g.pbWriteNext += sizeof(DLMNDX);
  522.         g.cbWrite++;
  523.         }
  524.     if (g.cbWrite >= MAX_WRITE_BUF || (pdlmndx == NULL && g.cbWrite > 0))
  525.         {
  526.         if (!FileWrite(g.hfTemp, g.pbWrite, sizeof(DLMNDX) * g.cbWrite, -1L))
  527.             return FALSE;
  528.  
  529.         g.pbWriteNext = g.pbWrite;
  530.         g.cbWrite = 0;
  531.         }
  532.     return TRUE;
  533. }
  534.  
  535.  
  536. //----------------------------------------------------------------------------
  537. //   Description:    Sort a partition
  538. //    Parameters:
  539. //       Returns:     1 = Already sorted
  540. //                         0 = Sorted
  541. //                        -1 = Failure
  542. //----------------------------------------------------------------------------
  543. static SHORT FN_L DataSortPartition(void)
  544. {
  545.     LONG lRec;
  546.     SIZET cRecords = (SIZET)(g.lEnd - g.lStart + 1);
  547.     BOOL fSorted = TRUE;
  548.     PBYTE pb = g.pbPartition;
  549.     PDLMNDX pdlmndx1, pdlmndx2;
  550.     DLMREC dlmrec;
  551.     PBYTE pbLast = NULL;
  552.  
  553.  
  554.     for (lRec = g.lStart; lRec <= g.lEnd; ++lRec)
  555.         {
  556.         pdlmndx1 = DataDelimitReadNdx(&g.dlm, lRec);
  557.  
  558.         if (pdlmndx1 == NULL                    // Read record
  559.         || DataDelimitReadRecordNdx(&g.dlm, lRec, pdlmndx1) == NULL)
  560.             return -1;
  561.  
  562.         DataDelimitConvert(&g.dlm, &dlmrec);
  563.         DataKey(&dlmrec, (PSZ)pb);
  564.                                                     // Store index info
  565.         pdlmndx2 = (PDLMNDX)(pb + g.cKeySort);
  566.         *pdlmndx2 = *pdlmndx1;
  567.  
  568.         if (pbLast)                                // Check if out of order
  569.             if (memcmp(pbLast, pb, g.cKeySort) > 0)
  570.                 fSorted = FALSE;
  571.  
  572.         pbLast = pb;                            // Move to next
  573.         pb += g.cKeyLen;
  574.         }
  575.     if (fSorted)                                // Already sorted!
  576.         return 1;
  577.                                                     // Sort the data
  578.     qsort(g.pbPartition, cRecords, g.cKeyLen, __keysort__);
  579.     return 0;
  580. }
  581.  
  582.  
  583. //----------------------------------------------------------------------------
  584. //   Description:
  585. //    Parameters:    
  586. //       Returns:    TRUE if successful.
  587. //----------------------------------------------------------------------------
  588. static BOOL FN_L DataSortTerminate(void)
  589. {
  590.     if (g.hfTemp >= 0)                        // Close temporary file
  591.         FileClose(g.hfTemp);
  592.  
  593.     if (FnameIsFile(g.szTemp))            // Delete temporary file
  594.         FnameDelete(g.szTemp);
  595.     if (g.plRecord)
  596.         MemFree(g.plRecord);
  597.     if (g.pbPartition)
  598.         MemFree(g.pbPartition);
  599.     if (g.pbWrite)
  600.         MemFree(g.pbWrite);
  601.     if (g.pcMerge)
  602.         MemFree(g.pcMerge);
  603.     DataDelimitClose(&g.dlm);                // Close delimited file
  604.     return TRUE;
  605. }
  606.  
  607.  
  608. //----------------------------------------------------------------------------
  609. //   Description:    Write sorted partition to disk
  610. //    Parameters:
  611. //       Returns:    TRUE if successful.
  612. //----------------------------------------------------------------------------
  613. static BOOL FN_L DataSortWrite(void)
  614. {
  615.     HF hf;
  616.     LONG lRecord;
  617.     FPOS fpos;
  618.     SIZET cbWrite;
  619.     PBYTE pb = g.pbPartition;
  620.     PDLMNDX pdlmndx = (PDLMNDX)g.pbPartition;
  621.  
  622.     DataDelimitFlush(&g.dlm);                // Flush index cache
  623.     for (lRecord = g.lStart; lRecord <= g.lEnd; ++lRecord)
  624.         {                                            // Write new indexes!
  625.         *pdlmndx = *(PDLMNDX)(pb + g.cKeySort);
  626.         pb += g.cKeyLen;
  627.         pdlmndx++;
  628.         }
  629.     hf = g.dlm.hfDelimitNdx;                // Write updated indexes!
  630.     fpos = g.lStart * sizeof(DLMNDX);
  631.     cbWrite = (SIZET)((g.lEnd - g.lStart + 1) * sizeof(DLMNDX));
  632.     return FileWrite(hf, g.pbPartition, cbWrite, fpos);
  633. }
  634. //----------------------------------------------------------------------------
  635. //------------------------------- End of File --------------------------------
  636. //----------------------------------------------------------------------------
  637.